package org.eoti.ai.genetic.gp.example1;

import org.eoti.ai.genetic.gp.*;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.ExampleMode;
import org.kohsuke.args4j.Option;

import java.io.IOException;
import java.math.BigInteger;
import java.util.concurrent.ConcurrentHashMap;

/**
 * The MIT License
 * <p/>
 * Copyright (c) 2009 Malachi de AElfweald (malachid@gmail.com)
 * <p/>
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * <p/>
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 * <p/>
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
public class ExampleSimulation
extends Simulation<BigInteger, BigInteger>
{
	@Option(name="-terminals", usage="number of terminals [default=10]")
	protected int numTerminals = 10;

	@Option(name="-maxInt", usage="maximum [exclusive] integer [default=100]")
	protected int maxInt = 100;

	@Option(name="-population", usage="population size [default=25]")
	protected int popSize = 25;

	@Option(name="-generations", usage="number of generations [default=100]")
	protected int numGen = 100;

	@Option(name="-minSize", usage="mininimum program size [default=3]")
	protected int minSize = 3;

	@Option(name="-maxSize", usage="maximum program size [default=30]")
	protected int maxSize = 30;

	@Option(name="-goal", usage="goal trying to reach [default=1703]")
	protected int goal = 1703;

	@Option(name="-accept", usage="offset from goal willing to stop at [default=0]")
	protected int acceptable = 0;

	@Option(name="-debug", usage="enable debug output [default=false]")
	protected boolean debug = false;

	public static void main(String ... args)
			throws IOException, CmdLineException, ProgramException
	{
		ExampleSimulation simulation = new ExampleSimulation(args);
	}

	@SuppressWarnings("unchecked")
	public ExampleSimulation(String ... args)
			throws CmdLineException, IOException, ProgramException
	{
		super();
		CmdLineParser cmdLineParser = new CmdLineParser(this);
		try{
			cmdLineParser.parseArgument(args);
//			if(WORKING_DIR == null)
//				throw new CmdLineException("-dir is a required option.");
		} catch(CmdLineException e){
			System.err.format("Error: %s\n", e.getMessage());
			System.err.format("USAGE: java %s [options...] arguments...\n", getClass().getName());
			cmdLineParser.printUsage(System.err);
			System.err.println();

			System.err.format(
				"EXAMPLE: java %s %s\n",
				getClass().getName(),
				cmdLineParser.printExample( ExampleMode.ALL)
			);

			System.exit(1);
		}

		Criteria<BigInteger,BigInteger> criteria = new Criteria<BigInteger,BigInteger>();
		criteria.addFunction(new Add());
		criteria.addFunction(new Subtract());
		criteria.addFunction(new Multiply());
		criteria.addFunction(new Divide());

		criteria.addTerminal(new Variable<BigInteger,BigInteger>("example1", "x"));
		criteria.addTerminal(new Variable<BigInteger,BigInteger>("example1", "g"));
		for(int i=2; i<numTerminals; i++)
			criteria.addTerminal(RandomInt.create( maxInt ));

//		criteria.addEvolution(new Reproduction());
		criteria.addEvolution(new Crossover());
		criteria.addEvolution(new Mutation(criteria));

		criteria.setPopulationSize(popSize);
		criteria.setMaxGenerations(numGen);

		criteria.setMinProgramSize(minSize);
		criteria.setMaxProgramSize(maxSize);
//		criteria.setAllowDuplicates(true);
		criteria.setMaxRepeats( 5 );

		criteria.setFitness(new ExampleFitness(BigInteger.valueOf(goal), criteria));
		criteria.setFitnessThreshold(BigInteger.valueOf(acceptable));

		criteria.setDebug(debug);

		setCriteria(criteria);

		Variable.set( "example1", "x", BigInteger.valueOf(1) );
		Program<BigInteger,BigInteger> best = simulate();

		System.out.format("\n\nRESULT:\n");

		System.out.format("Program: %s\nValue: %s\nFitness: %s\n", best, best.execute(), getCriteria().getFitness().getFitness( best ));
		System.out.format("Variables:\n");
		ConcurrentHashMap<String, BigInteger> variables = Variable.getMap( "example1" );
		for(String key : variables.keySet())
			System.out.format("%s:\t%s\n", key, variables.get(key));

		System.out.format("\n");

		System.out.format("\nHistory: ");
		for(BigInteger fitness : getHistory())
			System.out.format("%s, ", fitness);

		System.out.format("\n");
	}

	public Generation<BigInteger, BigInteger> createGeneration( int generationNum ){
		Variable.set("example1", "g", BigInteger.valueOf(generationNum));
		return new Generation<BigInteger,BigInteger>(generationNum, getCriteria() );
	}
}
